<?php

namespace App\Services;

use App\Model\Distributors;
use App\Model\Normal;
use GuzzleHttp\Client;
use Hyperf\Guzzle\CoroutineHandler;
use Hyperf\Guzzle\HandlerStackFactory;
use Hyperf\Utils\ApplicationContext;

class PushService
{
    private $url = 'https://www.nb-fuwu.com/xxyn/sjyycjk';

    private $account;

    private $pass ;

    private $publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn0MVxeKJM36xHVlivHBMfr5hY+UwLGGJ6H32zV5rruX38grA46CeZWncTEKq2QYrrvsyN6FU+gQeA9vXyeXypeAe8Hmv2dfVo2o+5vLO4E13KBoqT6iADEmfPkA/fkg32r/qzxK/DzMUBkkil8cqqH5+QSsLpvTB/F4FalNoB6k7Fov83Pqlc19f6nXDfWoFr4+fAONcwnBzSMr2JhLNIZsM2WPmu3JdxaVUX9a6lpSrlsFU1uDszWmLjI8nwKdudISqqGLDyaATlm88z4xaqVPlbPNeG4Ht+TNNRHGFXfD6XlTb9O9AhQ2S6Rmc/N+lg3pT8oJ9z9KndkdkuphHfQIDAQAB";

    public function __construct($account =  '17756008229', $pass = 'l_nQ:04Ysi76p&{L'){
        $this->pass = $pass;
        $this->account = $account;
    }

    public static function encrypt1($data,$publicKey) {
        $publicKeyResource = openssl_pkey_get_public($publicKey);
        if (!$publicKeyResource) {
            // Handle error: unable to get the public key
            return false;
        }
        $encrypted = "";
        $maxEncryptBlock = 117;
        $inputLen = strlen($data);
        $offSet = 0;
        $encryptedPart = "";
        while ($inputLen -$offSet > 0) {
            $chunk = substr($data, $offSet,$maxEncryptBlock);
            $cache = openssl_public_encrypt($chunk, $encryptedPart,$publicKeyResource);
            if ($cache === false) {
                // Handle encryption error
                return false;
            }
            $encrypted .=$encryptedPart;
            $offSet +=$maxEncryptBlock;
        }

        // No need to call openssl_free_key() in PHP 7.0.0 or later

        return base64_encode($encrypted);
    }

    public static function encrypt($data,$publicKey) {
        $publicKey = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        $publicKey = openssl_pkey_get_public($publicKey);
        $encrypted = "";
        $maxEncryptBlock = 117; // 与 Java 代码中的 MAX_ENCRYPT_BLOCK 保持一致
        $inputLen = strlen($data);
        $offSet = 0;
        while ($inputLen -$offSet > 0) {
            if ($inputLen -$offSet > $maxEncryptBlock) {
                $cache = openssl_public_encrypt(substr($data, $offSet,$maxEncryptBlock), $encryptedPart,$publicKey);
            } else {
                $cache = openssl_public_encrypt(substr($data, $offSet),$encryptedPart, $publicKey);
            }
            $encrypted .=$encryptedPart;
            $offSet +=$maxEncryptBlock;
        }
        return base64_encode($encrypted);

    }

    /**
     * 还原公钥，使用 X509EncodedKeySpec 构建公钥的规范
     * @param string $keyBytes 公钥的字节字符串
     * @return resource|false 返回公钥资源或 false（失败时）
     */
    public function restorePublicKey($keyBytes) {
        // 将字节数组转换为PEM格式的字符串
        $pemKey = chunk_split(base64_encode($keyBytes), 64, "\n");
        $pemKey = "-----BEGIN PUBLIC KEY-----\n" .$pemKey . "-----END PUBLIC KEY-----\n";

        // 使用openssl_pkey_get_public获取公钥资源
        $publicKeyResource = openssl_pkey_get_public($pemKey);

        if ($publicKeyResource === false) {
            // 错误处理，可以记录日志或者抛出异常
            while ($msg = openssl_error_string()) {
                error_log($msg);
            }
            return false;
        }

        // 将公钥资源转换为PEM格式字符串
        $publicKeyDetails = openssl_pkey_get_details($publicKeyResource);
        if ($publicKeyDetails === false) {
            // 错误处理
            while ($msg = openssl_error_string()) {
                error_log($msg);
            }
            return false;
        }

        // 返回PEM格式的公钥字符串
        return $publicKeyDetails['key'];
    }


    public function base64Encode($data) {
        return base64_encode($data);
    }


    public function base64Decode($data) {
        $decodedData = base64_decode($data, true);
//        if ($decodedData !== false) {
//            $encoding = mb_detect_encoding($decodedData, 'UTF-8, ISO-8859-1', true);
//            if ($encoding !== 'UTF-8') {
//                $decodedData = mb_convert_encoding($decodedData, 'UTF-8', $encoding);
//            }
        // 现在 $decodedData 应该是 UTF-8 编码的字符串了
        return $decodedData;
//        }
    }

    /**获取公钥
     * @return false|mixed|\Redis|string
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function getCode(){
        $path = '/data-api/auth/public-key';
        $publishRedis = 'PUBLIC_KEY_'.date('Y-m-d');
        if(redis()->exists($publishRedis)){
            return redis()->get($publishRedis);
        }
        $jsonData = json_encode(['code' => '17756008229', 'secret' => 'l_nQ:04Ysi76p&{L']);
        $base64Encrypted = $this->encrypt($jsonData,$this->publicKey);
        $data = self::curl_post($this->url.$path,$base64Encrypted);
        $data = json_decode($data,true);
        if($data['msg'] == ''){
            redis()->setex($publishRedis,3600*24,$data['data']);
            return $data['data'];
        }
        return '';
    }

    /**身份认证
     * @return mixed|string
     */
    public function getToken(){
        try{

            if(redis()->exists('CACHE_TOKEN_NINGBO')){
                return redis()->get('CACHE_TOKEN_NINGBO');
            }
            $path = '/data-api/auth/login';
            $microtime_float = microtime(true);
            $milliseconds = round(($microtime_float - floor($microtime_float)) * 1000);
            $seconds = floor($microtime_float);
            $millisecondTimestamp = $seconds * 1000 + $milliseconds;
            // 获取包含微秒的浮点数
            $timestamp = $millisecondTimestamp;
            //签名方式
            $data['str'] = "code=17756008229&secret=l_nQ:04Ysi76p&{L&timestamp={$timestamp}";
            $data['publicKey'] = $this->restorePublicKey($this->base64Decode($this->getCode()));
            $data['ciphertext'] = $this->encrypt1($data['str'],$data['publicKey']);
            $post['code'] = '17756008229';
            $post['secret'] = 'l_nQ:04Ysi76p&{L';
            $post['sign'] = $data['ciphertext'];
            $post['timestamp'] =  $timestamp;
            $post = json_encode($post);
            $postData = $this->encrypt($post,$this->publicKey);
            $result = self::curl_post($this->url.$path, $postData);
            $result = json_decode($result,true);
            if($result['msg'] == ''){
                redis()->setex('CACHE_TOKEN_NINGBO',24*60*60,$result['data']);
                return $result['data'];
            }
            return '';
        }catch (\Exception $e){
            return $e->getMessage();
        }
    }

    public function createOper($distributor){
        $path = '/data-api/OperatorInfo/push';
        $token = $this->getToken();
        $data['operatorDesc'] = '顺充新能源';
        $data['operatorName'] = '浙江顺充新能源';
        $data['operatorTel1'] = $distributor->phone;
        $post = json_encode($data);
        $postData = $this->encrypt($post,$this->publicKey);
        $result = self::curl_post($this->url.$path, $postData,$token);
        $result = json_decode($result,true);
        $result['local_request'] = $data;
        if($result['code'] == 401){
            redis()->del('CACHE_TOKEN_NINGBO');
            $this->createOper($distributor);
        }else{
            return $result;
        }
    }

    public function sendStation($station){
        $path= '/data-api/Site/push';
        $token = $this->getToken();
        $distributor = Distributors::where('id',$station->distributor_id)->first();
        $data['address'] = $station->address;
        $data['contact'] = $distributor->phone ;
        $data['districtCode'] = $station->districtCode;
        $data['latitude'] = $station->lat;
        $data['longitude'] = $station->lng;
        $data['operationDate'] = date('Y-m-d H:i:s',strtotime($station->created_at));
        $data['siteCode'] = $station->id;
        $data['siteName'] = $station->name;
        $data['siteScene'] = '0'.$station->siteScene;
        $data['siteType'] = '0'.$station->siteType;
        $data['fireFacilities'] = $station->fireFacilities;
        $data['canopyFlag'] = $station->canopyFlag;
        $post = json_encode($data);
        $postData = $this->encrypt($post,$this->publicKey);
        $result = self::curl_post($this->url.$path, $postData,$token);
        Normal::logger($result,'order-queue');
        $result = json_decode($result,true);
        if($result['code'] == 401){
            redis()->del('CACHE_TOKEN_NINGBO');
            $this->sendStation($station);
        }else{
            return $result;
        }
    }


    public function sendDevice($device){
        $path= '/data-api/Site/push';
        $token = $this->getToken();
        $data['deviceMode'] = 1;
        $data['deviceName'] = $device->sn;
        $data['partsNum'] = $device->channel;
        $data['pileCode'] = $device->id;
        $data['siteCode'] = $device->station_id;
        $post = json_encode($data);
        $postData = $this->encrypt($post,$this->publicKey);
        $result = self::curl_post($this->url.$path, $postData,$token);
        $result = json_decode($result,true);
        if($result['code'] == 401){
            redis()->del('CACHE_TOKEN_NINGBO');
            $this->sendDevice($device);
        }else{
            return $result;
        }
    }

    public function sendPort($data){
        $path = '/data-api/DevicePilePort/push';
        $token = $this->getToken();
        $post = json_encode($data);
        $postData = $this->encrypt($post,$this->publicKey);
        $result = self::curl_post($this->url.$path, $postData,$token);
        $result =  json_decode($result);
        if($result['code'] == 401){
            redis()->del('CACHE_TOKEN_NINGBO');
            $this->sendPort($data);
        }else{
            return $result;
        }
    }


    public function sendOrder($data){
        $path = '/data-api/Order/push';
        $token = $this->getToken();
        $post = json_encode($data);
        $postData = $this->encrypt($post,$this->publicKey);
        $result = self::curl_post($this->url.$path, $postData,$token);
        $result =  json_decode($result);
        if($result['code'] == 401){
            redis()->del('CACHE_TOKEN_NINGBO');
            $this->sendPort($data);
        }else{
            return $result;
        }
    }





    public static function curl_post($url, $data,$token= '',$type = 'body',$charset='UTF-8')
    {
        $container = ApplicationContext::getContainer();
        $handlerStackFactory =$container->get(HandlerStackFactory::class);
        $handlerStack =$handlerStackFactory->create();
        $client = new Client(['handler' => new CoroutineHandler($handlerStack)]);

        // 确保数据是字符串格式，如果$data是数组，则需要先编码成JSON字符串
        if (is_array($data)) {
            $jsonData = json_encode($data);
        } else {
            $jsonData =$data;
        }
        $response =$client->post($url, [
            'headers' => [
                'Content-Type' => 'application/json;charset='.$charset,
                'token'=>$token
            ],
            $type => $jsonData // 发送原始数据
        ]);
        return $response->getBody()->getContents();
    }

}
